package com.lazi.june.gateway.filter;

import com.lazi.june.common.base.RequestConst;
import com.lazi.june.gateway.config.GatewayProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * CORS 跨域过滤器
 * <p>
 * 处理跨域资源共享（CORS）请求，允许来自不同域的前端应用访问网关接口。
 * <p>
 * 主要功能：
 * 1. 为所有响应添加 CORS 相关的响应头
 * 2. 处理 OPTIONS 预检请求，直接返回 200
 * 3. 支持配置允许的源、方法、请求头等
 * <p>
 * CORS 工作原理：
 * - 简单请求：浏览器直接发送请求，服务器返回 CORS 响应头
 * - 预检请求：浏览器先发送 OPTIONS 请求，服务器返回允许的跨域配置
 * <p>
 * 配置示例：
 * <pre>
 * june:
 *   gateway:
 *     cors-enabled: true
 *     cors-allowed-origins:
 *       - https://example.com
 *       - http://localhost:3000
 *     cors-allowed-methods:
 *       - GET
 *       - POST
 *       - PUT
 *       - DELETE
 *     cors-allow-credentials: true
 *     cors-max-age: 3600
 * </pre>
 * <p>
 * 响应头说明：
 * - Access-Control-Allow-Origin: 允许的源
 * - Access-Control-Allow-Methods: 允许的 HTTP 方法
 * - Access-Control-Allow-Headers: 允许的请求头
 * - Access-Control-Allow-Credentials: 是否允许携带凭证
 * - Access-Control-Max-Age: 预检请求的缓存时间
 *
 * @author huliqian
 * @since 2025/1/15
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CorsFilter implements GlobalFilter, Ordered {

    /**
     * 网关配置属性
     */
    private final GatewayProperties props;

    /**
     * 过滤器核心逻辑：处理 CORS 跨域请求
     * <p>
     * 执行流程：
     * 1. 检查是否启用 CORS
     * 2. 为响应添加 CORS 相关响应头
     * 3. 如果是 OPTIONS 预检请求，直接返回 200
     * 4. 其他请求继续过滤器链
     *
     * @param exchange 当前请求的 ServerWebExchange
     * @param chain    过滤器链
     * @return Mono<Void> 异步响应
     */
    @NonNull
    @Override
    public Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull GatewayFilterChain chain) {
        // 1. 检查是否启用 CORS
        GatewayProperties.CorsConfig corsConfig = props.getCors();
        if (!corsConfig.isEnabled()) {
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();

        // 2. 获取请求的 Origin
        String origin = request.getHeaders().getOrigin();

        // 3. 设置 CORS 响应头
        // 3.1 Access-Control-Allow-Origin
        if (isOriginAllowed(origin)) {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
        } else if (corsConfig.getAllowedOrigins().contains("*")) {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        }

        // 3.2 Access-Control-Allow-Methods
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, String.join(", ", corsConfig.getAllowedMethods()));

        // 3.3 Access-Control-Allow-Headers
        // 如果配置为 "*"，则允许所有请求头，否则使用配置的请求头列表
        if (corsConfig.getAllowedHeaders().contains("*")) {
            // 允许所有请求头，优先返回客户端请求的 Access-Control-Request-Headers
            String requestHeaders = request.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
            if (requestHeaders != null && !requestHeaders.isBlank()) {
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);
            } else {
                // 如果客户端没有指定，则返回常用的请求头，包括 RequestConst 中定义的常量
                List<String> defaultHeaders = new ArrayList<>();
                defaultHeaders.add(HttpHeaders.CONTENT_TYPE);
                defaultHeaders.add(HttpHeaders.AUTHORIZATION);
                defaultHeaders.add(RequestConst.TOKEN);
                defaultHeaders.add(RequestConst.INTERNAL_SECRET);
                defaultHeaders.add(RequestConst.USER_INFO);
                defaultHeaders.add(RequestConst.TENANT_ID);
                defaultHeaders.add(RequestConst.APP_ID);
                defaultHeaders.add(RequestConst.VERSION);
                defaultHeaders.add(RequestConst.DEVICE_TYPE);
                defaultHeaders.add(RequestConst.PLATFORM);
                defaultHeaders.add(RequestConst.TRACE_ID);
                defaultHeaders.add(RequestConst.REQUEST_ID);
                defaultHeaders.add(RequestConst.FORWARDED_FOR);
                defaultHeaders.add(RequestConst.FORWARDED_PROTO);
                defaultHeaders.add(RequestConst.FORWARDED_HOST);
                
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, String.join(", ", defaultHeaders));
            }
        } else {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, String.join(", ", corsConfig.getAllowedHeaders()));
        }

        // 3.4 Access-Control-Allow-Credentials
        if (corsConfig.isAllowCredentials()) {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        }

        // 3.5 Access-Control-Max-Age
        headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, String.valueOf(corsConfig.getMaxAge()));

        // 3.6 Access-Control-Expose-Headers
        // 暴露自定义响应头给前端，包括 RequestConst 中定义的常用响应头
        List<String> exposeHeaders = new ArrayList<>();
        exposeHeaders.add(RequestConst.TRACE_ID);
        exposeHeaders.add(RequestConst.REQUEST_ID);
        exposeHeaders.add(RequestConst.USER_INFO);
        exposeHeaders.add(RequestConst.TENANT_ID);
        
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, String.join(", ", exposeHeaders));

        // 4. 处理 OPTIONS 预检请求
        if (HttpMethod.OPTIONS.equals(request.getMethod())) {
            response.setStatusCode(HttpStatus.OK);
            return Mono.empty();
        }

        // 5. 其他请求继续过滤器链
        return chain.filter(exchange);
    }

    /**
     * 检查请求的 Origin 是否在允许列表中
     * <p>
     * 支持精确匹配和通配符匹配
     *
     * @param origin 请求的 Origin
     * @return 是否允许
     */
    private boolean isOriginAllowed(String origin) {
        if (origin == null) {
            return false;
        }

        GatewayProperties.CorsConfig corsConfig = props.getCors();
        for (String allowedOrigin : corsConfig.getAllowedOrigins()) {
            // 精确匹配
            if (allowedOrigin.equals(origin)) {
                return true;
            }
            // 通配符匹配 (例如: https://*.example.com)
            if (allowedOrigin.contains("*")) {
                String pattern = allowedOrigin.replace(".", "\\.").replace("*", ".*");
                if (origin.matches(pattern)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 过滤器执行顺序
     * <p>
     * 返回 -200，在所有业务过滤器之前执行
     * <p>
     * 执行顺序参考：
     * - CorsFilter: -200 (CORS 跨域)
     * - BlacklistFilter: -110 (黑名单检查)
     * - JwtAuthFilter: -100 (认证)
     * - RequestBodyCacheFilter: -90 (请求体缓存)
     * - RateLimitFilter: -88 (限流)
     * - RequestLoggingFilter: -80 (日志)
     * <p>
     * CORS 必须最先执行，确保所有响应都包含 CORS 响应头
     *
     * @return 顺序值，越小越早执行
     */
    @Override
    public int getOrder() {
        // 在所有业务过滤器之前执行
        return -200;
    }
}